package thead;


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * 实现自己的堵塞队列
 */
class MyBlockingQueue{
    private int[] arrays = new int[1000];
    /*
        这里的三个元素要加 volatile 是因为 下面的操作会对这三个频繁的修改 防止出现内存可见性问题就 添加上了
     */
    private volatile int head = 0;// 头部
    private volatile int dest = 0;// 尾部
    private volatile int size = 0;

    // 插入
    public void add(int s) throws InterruptedException {
        synchronized (this) {
            if(size == arrays.length) {
                // 满了,那就应该堵塞
                    wait();
            }
            // 没满,那就继续添加
            arrays[dest++] = s;
            size++;
            if(dest == arrays.length) {
                // 这里是,dest 超过数组长度了,要回到0
                dest = 0;
            }
            notify();
        }
    }
    // 弹出
    public int take() throws InterruptedException {
        synchronized (this) {
            while (size == 0) { // 这里用 while 的原因是, 当wait 被 interrupt 唤醒后, 如果是if 就直接 结束循环了 ,但是当前队列里面还是空的 ,那就会出现异常了
                // 现在队列里面是空的,堵塞线程 当有元素添加进线程了之后再进行唤醒
                wait();
            }
            // 这里 队列里面不为空, 开始弹出元素
            int n = arrays[head++];
            size--;
            if (head == arrays.length) {
                head = 0;
            }
            notify();
            return n;
        }
    }
}



public class practice6_3 {
    public static void main(String[] args){
        MyBlockingQueue queue = new MyBlockingQueue();
        Thread t1 = new Thread(() ->{
            for(int i = 0; i < 3000;i++) {
                try {
                    Thread.sleep(1000);
                    queue.add(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("生产者: " + i);
            }
        });
        Thread t2 = new Thread(() -> {
           for(int i = 0; i < 3000;i++) {
               try {
                   System.out.println("消费者: " + queue.take());
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        t1.start();
        t2.start();
    }
}
